﻿// Copyright (c) 2012 - 2021 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2021-03-19 version 4.0.0 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "CommonPacket.h"
#include "ServerLoader.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CCommonHandler
bool CCommonHandler::Init(UInt uRank)
{
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(uRank);
    assert(sc.usType != COMMONT_NONE);

    switch (sc.usType)
    {
    case COMMONT_MASTER:
        {
            LOADER_CLASS_CREEATE(CServerLoader, Frame, UUID_OF(IMasterServer), m_FramePtr.Cast<CComponent>());
        }
        break;
    case COMMONT_SLAVE:
        {
            LOADER_CLASS_CREEATE(CServerLoader, Frame, UUID_OF(ISlaveServer), m_FramePtr.Cast<CComponent>());
        }
        break;
    case COMMONT_NODE:
        {
            LOADER_CLASS_CREEATE(CServerLoader, Frame, UUID_OF(INodeServer), m_FramePtr.Cast<CComponent>());
        }
        break;
    default: { }
    }
    if ((m_FramePtr == nullptr) || (m_FramePtr->Init(*this, GServerLoaderInst->m_Config, uRank) != RET_OKAY))
    {
        return false;
    }
    m_uRank = uRank;
    LOGV_INFO(GServerLoaderInst->m_FileLog, TF("初始化服务Type=%d, Rank=%d, Name=%s, SyncRank=%d, Id=%d, Queue=%d, Load=%d, Rate=%d, Routine=%s, Module=%s"),
              sc.usType, uRank, sc.strName.GetBuffer(), sc.usSync, sc.usId, sc.usQueue, sc.usLoad, sc.usRate, sc.strRoutine.GetBuffer(), sc.strModule.GetBuffer());
    return true;
}

void CCommonHandler::Exit(void)
{
    if (m_FramePtr != nullptr)
    {
        m_FramePtr->Exit();
        m_FramePtr = nullptr;
    }
    m_uRank = COMMONT_NONE;
}

bool CCommonHandler::Start(void)
{
    return m_FramePtr->Start();
}

void CCommonHandler::Stop(bool bUnlink)
{
    m_nLoop   = 0;
    m_nUpdate = 0;

    m_FramePtr->Stop(bUnlink);
}

UInt CCommonHandler::OnHandle(uintptr_t utEvent, uintptr_t utData, ULLong ullParam)
{
    switch (utEvent)
    {
    case PAKE_LINK:
        {
            OnLink((PAIR_INFO*)utData, (UInt)ullParam);
        }
        break;
    case PAKE_LINKACK:
        {
            OnLinkAck((Socket_t)utData, (UInt)ullParam);
        }
        break;
    case PAKE_UPDATE:
        {
            OnUpdate((PAIR_INFO*)utData, (UInt)ullParam);
        }
        break;
    case PAKE_UPDATEACK:
        {
            OnUpdateAck((PSERVER_DATA)utData, (UInt)ullParam);
        }
        break;
    case PAKE_SYNC:
        {
            OnSync((PSERVER_STATUS)utData, (UInt)ullParam);
        }
        break;
    case PAKE_SYNCACK:
        {
            OnSyncAck((PSERVER_CLUSTER)utData, (UInt)ullParam);
        }
        break;
    case PAKE_UNLINK:
        {
            OnUnlink((PAIR_INFO*)utData, (UInt)ullParam);
        }
        break;
    case PAKE_UNLINKACK:
        {
            OnUnlinkAck((Socket_t)utData, (UInt)ullParam);
        }
        break;
    case PAKE_TCPLISTEN:
        {
            OnTcpListen((Socket_t)utData, (UInt)ullParam);
        }
        break;
    case PAKE_TCPLISTEN_CLOSE:
        {
            OnTcpListenClose((Socket_t)utData, (UInt)ullParam);
        }
        break;
    case PAKE_TCPCONNECT:
        {
            OnTcpConnect((Socket_t)utData, (UInt)ullParam);
        }
        break;
    case PAKE_TCPCONNECT_CLOSE:
        {
            OnTcpConnectClose((Socket_t)utData, (UInt)ullParam);
        }
        break;
    default:
        {
            //assert(0);
        }
    }
    return RET_OKAY;
}

UInt CCommonHandler::OnHandle(uintptr_t, CEventBase&, ULLong)
{
    return RET_OKAY;
}

UInt CCommonHandler::OnHandle(uintptr_t, CStream&, ULLong)
{
    return RET_OKAY;
}

UInt CCommonHandler::OnHandle(uintptr_t, UInt)
{
    assert(m_FramePtr != nullptr);
    if (m_FramePtr->Update() == RET_OKAY)
    {
        ++m_nLoop;
        if (m_nLoop > STATUS_UPDATE_LOOP)
        {
            m_nLoop = 0;
            if (m_nUpdate >= STATUS_UPDATE_MAX)
            {
                m_nUpdate = 0;
                UpdateStatus();
                m_FramePtr->GetFileLog().Flush();
            }
            else
            {
                ++m_nUpdate;
            }
        }
    }
    return RET_OKAY;
}

void CCommonHandler::UpdateStatus(void)
{
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);
    SERVER_DATA& sd = m_FramePtr->GetData();

    CString strLoad;
    if (sd.IsOverLoad(false) == false)
    {
        strLoad.Format(TF("%d%%"), (sd.usLoad / COMMONC_TOPERCENT));
    }
    else if (sd.IsMayDay())
    {
        strLoad = TF("MayDay");
    }
    else if (sd.IsWarnLoad(true))
    {
        strLoad = TF("Warn");
    }
    else
    {
        strLoad = TF("Overload/Ready");
    }

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===UPDATE-STATUS=== 1. Self-Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d, Queue-Count=%d"),
                         sc.usType, m_uRank, sd.usId, strLoad.GetBuffer(), sd.usRate, sd.uTotal, sd.uOnline, m_FramePtr->GetSignalEvent().GetCount());
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());

    Int nCount = 0;
    strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===UPDATE-STATUS=== 2. "), sc.usType, m_uRank);

    for (UInt i = (m_uRank + COMMONR_MIN); i < COMMONR_MAX; ++i)
    {
        const SERVER_CLUSTER& Cluster = m_FramePtr->GetCluster(i);
        if (Cluster.Status.usLoad == 0)
        {
            continue;
        }
        ++nCount;
        strInfo.AppendFormat(TF(" Cluster-Rank=%d, Cluster-Servers=%d, Total=%d, Online=%d"),
                             i, Cluster.Status.usLoad, Cluster.Status.uTotal, Cluster.Status.uOnline);
    }
    if (nCount > 0)
    {
        LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
    }

    if (sc.usType == COMMONT_SLAVE)
    {
        nCount = 0;
        strInfo = sc.strName;
        strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===UPDATE-STATUS==="), sc.usType, m_uRank);

        for (UInt i = (m_uRank + COMMONR_MIN); i < COMMONR_MAX; ++i)
        {
            const SERVER_CLUSTER& Cluster = m_FramePtr->GetCluster(i, false);
            if (Cluster.Status.usLoad == 0)
            {
                continue;
            }
            ++nCount;
            strInfo.AppendFormat(TF(" SLAVE-LOCAL Cluster-Rank=%d, Cluster-Servers=%d, Total=%d, Online=%d"),
                                i, Cluster.Status.usLoad, Cluster.Status.uTotal, Cluster.Status.uOnline);
        }
        if (nCount > 0)
        {
            LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
        }
    }
}

// 注册
void CCommonHandler::OnLink(PAIR_INFO* pInfo, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);

    CString strLoad;
    if (pInfo->m_V.IsOverLoad(false) == false)
    {
        strLoad.Format(TF("%d%%"), (pInfo->m_V.usLoad / COMMONC_TOPERCENT));
    }
    else if (pInfo->m_V.IsMayDay())
    {
        strLoad = TF("MayDay");
    }
    else if (pInfo->m_V.IsWarnLoad(true))
    {
        strLoad = TF("Warn");
    }
    else
    {
        strLoad = TF("Overload/Ready");
    }

    CStrAddr strRemoteAddr;
    GServerLoaderInst->m_NetworkPtr->GetAddr((Socket_t)pInfo->m_K, strRemoteAddr.strIp, strRemoteAddr.usPort);

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ListenAddr=%s[%d], ===LINK=== Connect-Rank=%d, Socket=%p, RemoteAddr=%s[%d], Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d, SyncMask=%x, StrAddr=%s[%d], Routine=%s"),
                         sc.usType, m_uRank, sc.Listen.strIp.GetBuffer(), sc.Listen.usPort, uRank, pInfo->m_K, strRemoteAddr.strIp.GetBuffer(), strRemoteAddr.usPort, pInfo->m_V.usId, strLoad.GetBuffer(),
                         pInfo->m_V.usRate, pInfo->m_V.uTotal, pInfo->m_V.uOnline, pInfo->m_V.uSync, pInfo->m_V.StrAddr.strIp.GetBuffer(), pInfo->m_V.StrAddr.usPort, pInfo->m_V.strRoutine.GetBuffer());
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// 注册完成
void CCommonHandler::OnLinkAck(Socket_t sSocket, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& scConnect = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);
    const SERVER_CONFIG& scListen  = GServerLoaderInst->m_Config.GetServerConfig(uRank);

    CStrAddr strLocalAddr;
    GServerLoaderInst->m_NetworkPtr->GetAddr(sSocket, strLocalAddr.strIp, strLocalAddr.usPort, false);

    CString strInfo = scConnect.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ConnectAddr=%s[%d], ===LINK-ACK=== Listen-Rank=%d, Socket=%p, ListenAddr=%s[%d], Type=%d, Name=%s"),
                         scConnect.usType, m_uRank, strLocalAddr.strIp.GetBuffer(), strLocalAddr.usPort, uRank, sSocket,
                         scListen.Listen.strIp.GetBuffer(), scListen.Listen.usPort, scListen.usType, scListen.strName.GetBuffer());
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// 更新
void CCommonHandler::OnUpdate(PAIR_INFO* pInfo, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);

    CString strLoad;
    if (pInfo->m_V.IsOverLoad(false) == false)
    {
        strLoad.Format(TF("%d%%"), (pInfo->m_V.usLoad / COMMONC_TOPERCENT));
    }
    else if (pInfo->m_V.IsMayDay())
    {
        strLoad = TF("MayDay");
    }
    else if (pInfo->m_V.IsWarnLoad(true))
    {
        strLoad = TF("Warn");
    }
    else
    {
        strLoad = TF("Overload/Ready");
    }

    CStrAddr strRemoteAddr;
    GServerLoaderInst->m_NetworkPtr->GetAddr((Socket_t)pInfo->m_K, strRemoteAddr.strIp, strRemoteAddr.usPort);

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ListenAddr=%s[%d], ===UPDATE=== Connect-Rank=%d, Socket=%p, RemoteAddr=%s[%d], Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d"),
                         sc.usType, m_uRank, sc.Listen.strIp.GetBuffer(), sc.Listen.usPort, uRank, pInfo->m_K, strRemoteAddr.strIp.GetBuffer(), strRemoteAddr.usPort,
                         pInfo->m_V.usId, strLoad.GetBuffer(), pInfo->m_V.usRate, pInfo->m_V.uTotal, pInfo->m_V.uOnline);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// 更新状态
void CCommonHandler::OnUpdateAck(PSERVER_DATA pData, UInt uRank)
{
    ++m_nUpdate;
    assert(m_uRank == uRank);
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(uRank);

    CString strLoad;
    if (pData->IsOverLoad(false) == false)
    {
        strLoad.Format(TF("%d%%"), (pData->usLoad / COMMONC_TOPERCENT));
    }
    else if (pData->IsMayDay())
    {
        strLoad = TF("MayDay");
    }
    else if (pData->IsWarnLoad(true))
    {
        strLoad = TF("Warn");
    }
    else
    {
        strLoad = TF("Overload/Ready");
    }

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===UPDATE-ACK=== Self-Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d"),
                         sc.usType, uRank, sc.usId, strLoad.GetBuffer(), pData->usRate, pData->uTotal, pData->uOnline);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// 同步状态
void CCommonHandler::OnSync(PSERVER_STATUS pStatus, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===SYNC=== Cluster-Rank=%d, Cluster-Servers=%d, Total=%d, Online=%d"),
                         sc.usType, m_uRank, uRank, pStatus->usLoad, pStatus->uTotal, pStatus->uOnline);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// 同步接收
void CCommonHandler::OnSyncAck(PSERVER_CLUSTER pCluster, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===SYNC-ACK=== Cluster-Rank=%d, Cluster-Servers=%d, Total=%d, Online=%d"),
                         sc.usType, m_uRank, uRank, pCluster->Status.usLoad, pCluster->Status.uTotal, pCluster->Status.uOnline);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
    {
        CSyncLockWaitScope scope(pCluster->Lock);
        for (PINDEX index = pCluster->Info.GetFirstIndex(); index != nullptr; )
        {
            PAIR_INFO* pPair = pCluster->Info.GetNext(index);
            OnSyncAckStatus(sc, pPair, uRank);
        }
    }
}

void CCommonHandler::OnSyncAckStatus(const SERVER_CONFIG& sc, PAIR_INFO* pInfo, UInt uRank)
{
    CString strLoad;
    if (pInfo->m_V.IsOverLoad(false) == false)
    {
        strLoad.Format(TF("%d%%"), (pInfo->m_V.usLoad / COMMONC_TOPERCENT));
    }
    else if (pInfo->m_V.IsMayDay())
    {
        strLoad = TF("MayDay");
    }
    else if (pInfo->m_V.IsWarnLoad(true))
    {
        strLoad = TF("Warn");
    }
    else
    {
        strLoad = TF("Overload/Ready");
    }

    CString strInfo = sc.strName;
    switch (pInfo->m_V.usUpdate)
    {
    case SERVERU_LINK:
        {
            strInfo.AppendFormat
            (TF("[Type=%d, Rank=%d] ===SYNC-ACK--LINK=== Rank=%d, Socket=%p, Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d, SyncMask=%x, StrAddr=%s[%d], Routine=%s"),
             sc.usType, m_uRank, uRank, pInfo->m_K, pInfo->m_V.usId, strLoad.GetBuffer(), pInfo->m_V.usRate, pInfo->m_V.uTotal, pInfo->m_V.uOnline,
             pInfo->m_V.uSync, pInfo->m_V.StrAddr.strIp.GetBuffer(), pInfo->m_V.StrAddr.usPort, pInfo->m_V.strRoutine.GetBuffer());
        }
        break;
    case SERVERU_SYNC:
        {
            strInfo.AppendFormat
            (TF("[Type=%d, Rank=%d] ===SYNC-ACK--SYNC=== Rank=%d, Socket=%p, Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d"),
             sc.usType, m_uRank, uRank, pInfo->m_K, pInfo->m_V.usId, strLoad.GetBuffer(), pInfo->m_V.usRate, pInfo->m_V.uTotal, pInfo->m_V.uOnline);
        }
        break;
    case SERVERU_UNLINK:
        {
            strInfo.AppendFormat
            (TF("[Type=%d, Rank=%d] ===SYNC-ACK--UNLINK=== Rank=%d, Socket=%p, Id=%d"),
             sc.usType, m_uRank, uRank, pInfo->m_K, pInfo->m_V.usId);
        }
        break;
    default: {}
    }
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// 注销
void CCommonHandler::OnUnlink(PAIR_INFO* pInfo, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===UNLINK=== Connect-Rank=%d, Socket=%p, Id=%d"),
                         sc.usType, m_uRank, uRank, pInfo->m_K, pInfo->m_V.usId);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// 注销完成
void CCommonHandler::OnUnlinkAck(Socket_t sSocket, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& scConnect = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);
    const SERVER_CONFIG& scListen  = GServerLoaderInst->m_Config.GetServerConfig(uRank);

    CString strInfo = scConnect.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===UNLINK-ACK=== Listen-Rank=%d, Socket=%p, Type=%d, Name=%s"),
                         scConnect.usType, m_uRank, uRank, sSocket, scListen.usType, scListen.strName.GetBuffer());
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// TCP监听
void CCommonHandler::OnTcpListen(Socket_t sSocket, UInt uRank)
{
    ++m_nUpdate;
    assert(m_uRank == uRank);
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(uRank);

    SERVER_DATA& sd = m_FramePtr->GetData();

    CString strLoad;
    if (sd.IsOverLoad(false) == false)
    {
        strLoad.Format(TF("%d%%"), (sd.usLoad / COMMONC_TOPERCENT));
    }
    else if (sd.IsMayDay())
    {
        strLoad = TF("MayDay");
    }
    else if (sd.IsWarnLoad(true))
    {
        strLoad = TF("Warn");
    }
    else
    {
        strLoad = TF("Overload/Ready");
    }

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===TCP-LISTEN=== Self-Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d"),
                         sc.usType, uRank, sd.usId, strLoad.GetBuffer(), sd.usRate, sd.uTotal, sd.uOnline);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// TCP监听关闭
void CCommonHandler::OnTcpListenClose(Socket_t sSocket, UInt uRank)
{
    ++m_nUpdate;
    assert(m_uRank == uRank);
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(uRank);

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===TCP-LISTEN-CLOSE=== Self-Id=%d"), sc.usType, uRank, sc.usId);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// TCP链接
void CCommonHandler::OnTcpConnect(Socket_t sSocket, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);

    SERVER_DATA& sd = m_FramePtr->GetData();

    CString strLoad;
    if (sd.IsOverLoad(false) == false)
    {
        strLoad.Format(TF("%d%%"), (sd.usLoad / COMMONC_TOPERCENT));
    }
    else if (sd.IsMayDay())
    {
        strLoad = TF("MayDay");
    }
    else if (sd.IsWarnLoad(true))
    {
        strLoad = TF("Warn");
    }
    else
    {
        strLoad = TF("Overload/Ready");
    }

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===TCP-CONNECT=== Listen-Rank=%d, Self-Id=%d, Load=%s, Rate=%d, Total=%d, Online=%d"),
                         sc.usType, m_uRank, uRank, sd.usId, strLoad.GetBuffer(), sd.usRate, sd.uTotal, sd.uOnline);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

// TCP链接关闭
void CCommonHandler::OnTcpConnectClose(Socket_t sSocket, UInt uRank)
{
    ++m_nUpdate;
    const SERVER_CONFIG& sc = GServerLoaderInst->m_Config.GetServerConfig(m_uRank);

    CString strInfo = sc.strName;
    strInfo.AppendFormat(TF("[Type=%d, Rank=%d] ===TCP-CONNECT-CLOSE=== Listen-Rank=%d, Self-Id=%d"), sc.usType, m_uRank, uRank, sc.usId);
    LOG_INFO(GServerLoaderInst->m_FileLog, strInfo.GetBuffer());
}

